/**
 * Project Name: meatball-dowload-logs
 * Package Name: com.meatball
 * File Name: SSHRemoteCall
 * Date: 2021/2/2 19:58
 * User: 張翔宇
 * Copyright (c) 2021, zhang.xiangyu@foxmail.com All Rights Reserved.
 */
package com.meatball;

import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.util.Properties;
import java.util.Vector;

/**
 * 说明:exec用于执行命令;sftp用于文件处理
 * @title: SshRemoteCall.java
 * @author: 張翔宇
 * @date: 2021/2/2 19:58
 * @version: V3.0
 */
@Slf4j
public class SshRemoteCall {
    /**
     * 私有的对象
     */
    private static SshRemoteCall sshRemoteCall;

    /**
     * 私有构造方法
     */
    private SshRemoteCall() { }

    /**
     * 懒汉式,线程不安全,适合单线程
     * @return
     */
    public static SshRemoteCall getInstance() {
        if (sshRemoteCall == null) {
            sshRemoteCall = new SshRemoteCall();
        }
        return sshRemoteCall;
    }

    /**
     * 懒汉式,线程安全,适合多线程
     * @return
     */
    public static synchronized SshRemoteCall getInstanceSync() {
        if (sshRemoteCall == null) {
            sshRemoteCall = new SshRemoteCall();
        }
        return sshRemoteCall;
    }

    /**
     * 默认端口号
     */
    private static final int DEFAULT_PORT = 22;
    /**
     * 端口号
     */
    private int port;
    /**
     * ip地址
     */
    private static String ipAddress = "182.92.75.11";
    /**
     * 账户
     */
    private static String userName = "root";
    /**
     * 密码
     */
    private static String password = "HejY6anZgDMRYONgBvFC";
    /**
     * JSCH session
     */
    private Session session;
    /**
     * 是否登陆
     */
    private boolean isLogin = false;

    /**
     * 构造方法,方便直接传入ipAddress,userName,password进行调用
     * @param ipAddress
     * @param port
     * @param userName
     * @param password
     */
    public SshRemoteCall(String ipAddress, int port, String userName, String password) {
        super();
        SshRemoteCall.ipAddress = ipAddress;
        SshRemoteCall.userName = userName;
        SshRemoteCall.password = password;
        this.port = port;
    }

    /**
     * 构造方法,可以直接使用DEFAULT_PORT
     * @param ipAddress
     * @param userName
     * @param password
     */
    public SshRemoteCall(String ipAddress, String userName, String password) {
        this(ipAddress, DEFAULT_PORT, userName, password);
    }

    /**
     * 远程登陆
     * @title: sshRemoteCallLogin
     * @param ipAddress 
     * @param userName 
     * @param password
     * @return: void 返回类型 
     * @author: 張翔宇
     * @date: 2021/2/2 20:57 
     */
    public void sshRemoteCallLogin(String ipAddress, String userName, String password) throws Exception {
        // 如果登录就直接返回
        if (isLogin) {
            return;
        }
        // 创建jSch对象
        JSch jSch = new JSch();
        try {
            // 获取到jSch的session, 根据用户名、主机ip、端口号获取一个Session对象
            session = jSch.getSession(userName, ipAddress, DEFAULT_PORT);
            // 设置密码
            session.setPassword(password);
            // 通过session建立连接
            Properties config = new Properties();
            config.put("StrictHostKeyChecking", "no");
            // 为Session对象设置properties
            session.setConfig(config);
            // 通过session建立连接
            session.connect();
        } catch (Exception e) {
            // 设置登录状态为false
            isLogin = false;
            throw new Exception("主机登录失败, IP = " + ipAddress + ", USERNAME = " + userName + ", Exception:" + e.getMessage());
        }
    }

    /**
     * 关闭连接
     * @title: closeSession
     * @return: void 返回类型
     * @author: 張翔宇
     * @date: 2021/2/2 21:09
     */
    public void closeSession() {
        // 调用session的关闭连接的方法
        if (session != null) {
            // 如果session不为空,调用session的关闭连接的方法
            session.disconnect();
        }
    }

    /**
     * 执行相关的命令
     * @title: execCommand
     * @param command 
     * @return: void 返回类型 
     * @author: 張翔宇
     * @date: 2021/2/2 21:10 
     */
    public void execCommand(String command) throws IOException {
        // 输入流(读)
        InputStream in = null;
        // 定义channel变量
        Channel channel = null;
        try {
            // 如果命令command不等于null
            if (command != null) {
                // 打开channel
                // 说明：exec用于执行命令;sftp用于文件处理
                channel = session.openChannel("exec");
                // 设置command
                ((ChannelExec) channel).setCommand(command);
                // channel进行连接
                channel.connect();
                // 获取到输入流
                in = channel.getInputStream();
                // 执行相关的命令
                String processDataStream = processDataStream(in);
                // 打印相关的命令
                log.info("1、打印相关返回的命令: {}", processDataStream);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                in.close();
            }
            if (channel != null) {
                channel.disconnect();
            }
        }
    }
    
    /**
     * 对将要执行的linux的命令进行遍历
     * @title: processDataStream
     * @param in 
     * @return: java.lang.String 返回类型 
     * @author: 張翔宇
     * @date: 2021/2/2 21:16 
     */
    public String processDataStream(InputStream in) throws Exception {
        StringBuffer sb = new StringBuffer();
        BufferedReader br = new BufferedReader(new InputStreamReader(in));
        String result = "";
        try {
            while ((result = br.readLine()) != null) {
                sb.append(result);
            }
        } catch (Exception e) {
            throw new Exception("获取数据流失败: " + e);
        } finally {
            br.close();
        }
        return sb.toString();
    }
    
    /**
     * 上传文件 可参考:https://www.cnblogs.com/longyg/archive/2012/06/25/2556576.html
     * @title: uploadFile
     * @param directory 
     * @param uploadFile
     * @return: void 返回类型 
     * @author: 張翔宇
     * @date: 2021/2/2 21:19 
     */
    public void uploadFile(String directory, String uploadFile) {
        try {
            // 打开channelSftp
            ChannelSftp channelSftp = (ChannelSftp) session.openChannel("sftp");
            // 远程连接
            channelSftp.connect();
            // 创建一个文件名称问uploadFile的文件
            File file = new File(uploadFile);
            // 将文件进行上传(sftp协议)
            // 将本地文件名为src的文件上传到目标服务器,目标文件名为dst,若dst为目录,则目标文件名将与src文件名相同.
            // 采用默认的传输模式:OVERWRITE
            channelSftp.put(new FileInputStream(file), directory, ChannelSftp.OVERWRITE);
            // 切断远程连接
            channelSftp.exit();
            log.info("2、{} 文件上传成功.....", file.getName());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 下载文件 采用默认的传输模式：OVERWRITE
     * @title: fileDownload
     * @param src linux服务器文件地址
     * @param dst 本地存放地址
     * @return: void 返回类型 
     * @author: 張翔宇
     * @date: 2021/2/2 21:24 
     */
    public void fileDownload(String src, String dst) throws JSchException, SftpException {
        // src 是linux服务器文件地址,dst 本地存放地址
        ChannelSftp channelSftp = (ChannelSftp) session.openChannel("sftp");
        // 远程连接
        channelSftp.connect();
        // 下载文件,多个重载方法
        channelSftp.get(src, dst);
        // 切断远程连接,quit()等同于exit(),都是调用disconnect()
        channelSftp.quit();
        log.info("3、{} 下载文件成功……", src);
    }

    public InputStream fileDownload(String src) throws JSchException, SftpException {
        // src 是linux服务器文件地址,dst 本地存放地址
        ChannelSftp channelSftp = (ChannelSftp) session.openChannel("sftp");
        // 远程连接
        channelSftp.connect();
        // 下载文件,多个重载方法
        InputStream inputStream = channelSftp.get(src);
        // 切断远程连接,quit()等同于exit(),都是调用disconnect()
//        channelSftp.quit();
        log.info("3、{} 下载文件成功……", src);
        return inputStream;
    }

    /**
     * 删除文件
     * @title: deleteFile
     * @param directoryFile 要删除的文件
     * @return: void 返回类型 
     * @author: 張翔宇
     * @date: 2021/2/2 21:26 
     */
    public void deleteFile(String directoryFile) throws SftpException, JSchException {
        // 打开openChannel的sftp
        ChannelSftp channelSftp = (ChannelSftp) session.openChannel("sftp");
        // 远程连接
        channelSftp.connect();
        // 删除文件
        channelSftp.rm(directoryFile);
        // 切断远程连接
        channelSftp.exit();
        log.info("4、{} 删除的文件……", directoryFile);
    }

    /**
     * 列出目录下的文件
     * @title: listFiles
     * @param directory 要列出的目录
     * @return: java.util.Vector 返回类型 
     * @author: 張翔宇
     * @date: 2021/2/2 21:32 
     */
    public Vector listFiles(String directory) throws JSchException, SftpException {
        ChannelSftp channelSftp = (ChannelSftp) session.openChannel("sftp");
        // 远程连接
        channelSftp.connect();
        // 显示目录信息
        Vector ls = channelSftp.ls(directory);
        log.info("5、{} ", ls);
        // 切断连接
        channelSftp.exit();
        return ls;
    }

    public static void main(String[] args) {
        try {
            // 1、首先远程连接ssh
            getInstance().sshRemoteCallLogin(ipAddress, userName, password);
            // 打印信息
            log.info("0、连接服务器，ip地址：{}，账号：{}，连接成功", ipAddress, userName);
            // 查看内存的使用情况
//            String command = "free ";
//            SshRemoteCall.getInstance().execCommand(command);

            // 展示目录下的文件信息
            String lsDirectory = "/home/workspace/logs/account-server";
            Vector ls = getInstance().listFiles(lsDirectory);
            ls.forEach(item -> {
                String line = item.toString();
                String name = line.substring(line.lastIndexOf(" "));
                System.out.println(name);
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
